﻿using FDSimpleModelGenerator.Common;
using FDSimpleModelGenerator.IRepositories;
using FDSimpleModelGenerator.IServices;
using FDSimpleModelGenerator.Repositories;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using FDSimpleModelGenerator.Services.Mappers;

namespace FDSimpleModelGenerator.Services
{
    /// <summary>
    /// 使用公共服务
    /// </summary>
    public static class CommonServiceExtensions
    {
        /// <summary>
        /// 使用公共服务
        /// </summary>
        /// <param name="services"></param>
        /// <returns></returns>
        public static IServiceCollection AddCommonServices(this IServiceCollection services)
        {
            services.AddCommon();
            services.UsingRepositories();
            services.AddSingleton<IModelGenerateEngine, ModelGenerateEngine>();
            services.AddSingleton<IGenerateDbSql, GenerateDbSql>();

            services.AddSingleton<IDataTypeMapperService, CSharpDataTypeMapperService>();
            services.AddSingleton<IDataTypeMapperService, JavaDataTypeMapperService>();
            services.AddSingleton<IDataTypeMapperService, JdbcTypeDataTypeMapperService>();

            services.LoadDefaultRepoAndServices();
            //services.AddTransient<IClassTemplateRepository, ClassTemplateRepository>();
            //services.AddTransient<IClassTemplateCategoryRepository, ClassTemplateCategoryRepository>();
            //services.AddTransient<IClassTemplateCategoryService, ClassTemplateCategoryService>();
            //services.AddTransient<IClassTemplateService, ClassTemplateService>();
            //services.AddTransient<IBasicRepository<SysUser>, SysUserRepository>();
            //services.AddTransient<ISysUserRepository, SysUserRepository>();
            //services.AddTransient<ISysUserService, SysUserService>();


            return services;
        }

        /// <summary>
        /// 加载Repositories & Serivces
        /// </summary>
        /// <param name="services"></param>
        public static void LoadDefaultRepoAndServices(this IServiceCollection services)
        {
            var repositoryTypes = typeof(BasicRepository<>).Assembly.GetExportedTypes();
            var serviceTypes = typeof(BasicService<>).Assembly.GetExportedTypes();

            var repoTypes = GetGenericTypeOf(repositoryTypes, typeof(IBasicRepository<>)); //repositoryTypes.Where(t => t.IsClass && !t.IsAbstract && t.GetInterfaces().Any(t => t.IsInstanceOfType(typeof(IBasicRepository<>)))).ToList();
            var servTypes = GetGenericTypeOf(serviceTypes, typeof(IBasicService<>));
            services.AddInterTypeInject(repoTypes).
                AddInterTypeInject(servTypes);
        }

        /// <summary>
        /// 添加泛型接口与类型实现注入
        /// </summary>
        /// <param name="services"></param>
        /// <param name="types"></param>
        /// <returns></returns>
        private static IServiceCollection AddInterTypeInject(this IServiceCollection services, List<Type> types)
        {
            types.ForEach(t =>
            {
                var tInterfaces = t.GetInterfaces();
                foreach (var interType in tInterfaces)
                {
                    services.AddTransient(interType, t);
                }
            });
            return services;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sources"></param>
        /// <param name="interGenericType">通用泛型接口类型，如：IBasicRepository<></param>
        /// <returns></returns>
        private static List<Type> GetGenericTypeOf(IEnumerable<Type> sources, Type interGenericType)
        {
            var reps = new List<Type>();
            foreach (var t in sources)
            {
                var tas = t.BaseType.GenericTypeArguments;
                if (tas.Length > 0)
                {
                    var tb = interGenericType.MakeGenericType(tas);
                    if (tb.IsAssignableFrom(t))
                    {
                        reps.Add(t);
                    }
                }
            }
            return reps;
        }
    }
}
